﻿using System;
using System.Linq;
using System.Threading.Tasks;
using Common;
using Microsoft.AspNetCore.Mvc;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using WqLunTan.Filters;
using WqLunTan.Models.DataBase;
using System.Threading;
using WqLunTan.DBContext;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using HtmlAgilityPack;
using Newtonsoft.Json;
using WqLunTan.ConstConfig;
using WqLunTan.Models.ViewModel;
using WqLunTan.Areas.Manage.Models;

namespace WqLunTan.Areas.Manage.Controllers
{
    /// <summary>
    /// 后台爬虫控制器
    /// </summary>
    [Auth(AuthType = WqUserAuth.Manage)]
    public class CrawlerController : BaseManageController
    {

        /// <summary>
        /// 原子操作标记
        /// </summary>
        private static int flag = 0;

        /// <summary>
        /// 查看标签列表页, 自动抓取文章
        /// </summary>
        /// <returns></returns>
        public IActionResult TagList()
        {
            var tagConfig = DbContext.SiteConfig.Where(x => x.config_key.Equals(ConfigKey.ArticleCrawleTagList)).SingleOrDefault();
            if (tagConfig == null)
            {
                tagConfig = new site_config()
                {
                    config_key = ConfigKey.ArticleCrawleTagList,
                    create_time = DateTimeOffset.UtcNow,
                    last_update = DateTimeOffset.UtcNow,
                    uid = LoginUser.Id
                };
                var tagList = new List<ArticleCrawlerUrlTagConfig>();
                tagList.Add(new ArticleCrawlerUrlTagConfig() { TagName = "命理", TagUrl = "https://www.sohu.com/tag/67632" });
                tagList.Add(new ArticleCrawlerUrlTagConfig() { TagName = "八字", TagUrl = "https://www.sohu.com/tag/68143" });
                tagList.Add(new ArticleCrawlerUrlTagConfig() { TagName = "面相", TagUrl = "https://www.sohu.com/tag/67539" });
                tagList.Add(new ArticleCrawlerUrlTagConfig() { TagName = "手相", TagUrl = "https://www.sohu.com/tag/68031" });
                tagList.Add(new ArticleCrawlerUrlTagConfig() { TagName = "风水", TagUrl = "https://www.sohu.com/tag/67483" });
                tagConfig.config_value = JsonConvert.SerializeObject(tagList);
                DbContext.SiteConfig.Add(tagConfig);
                DbContext.SaveChanges();
            }
            ViewData["tag"] = tagConfig;
            ViewData["list"] = JsonConvert.DeserializeObject<List<ArticleCrawlerUrlTagConfig>>(tagConfig.config_value);

            return View();
        }

        /// <summary>
        /// 显示爬虫获取的文章内容
        /// </summary>
        /// <returns></returns>
        public IActionResult SaveList(string search, int pageIndex = 1, int pageSize = 20)
        {
            List<article_crawler> list;

            var query = DbContext.ArticleCrawls.Where(x => x.is_download && x.is_save);
            var page = new PageModel() { CurrentPageIndex = pageIndex };

            if (!string.IsNullOrWhiteSpace(search))
            {
                query = query.Where(x => x.title.Contains(search) || x.url.Contains(search) || x.tag.Contains(search));
                page.TotalCount = query.Count();
                list = query.OrderByDescending(x => x.publish_date).ThenByDescending(x => x.create_time).Skip(pageSize * pageIndex - pageSize).Take(pageSize).ToList();
            }
            else
            {
                page.TotalCount = query.Count();
                list = query.OrderByDescending(x => x.publish_date).ThenByDescending(x => x.create_time).Skip(pageSize * pageIndex - pageSize).Take(pageSize).ToList();
            }

            var cate = DbContext.art_category.OrderByDescending(x => x.is_show).ThenBy(x => x.sort).ThenBy(x => x.create_time).ToList();
            ViewData["page"] = page;
            ViewData["list"] = list;
            ViewData["is_save"] = true;
            ViewData["cate"] = cate;
            ViewData["save-count"] = DbContext.ArticleCrawls.Where(x => x.is_download && x.is_save).Count();
            ViewData["not-save-count"] = DbContext.ArticleCrawls.Where(x => x.is_download && x.is_save == false).Count();

            return View("List");
        }

        /// <summary>
        /// 显示爬虫获取的文章内容
        /// </summary>
        /// <returns></returns>
        public IActionResult List(string search, int pageIndex = 1, int pageSize = 20)
        {
            List<article_crawler> list;

            var query = DbContext.ArticleCrawls.Where(x => x.is_download && x.is_save == false);
            var page = new PageModel() { CurrentPageIndex = pageIndex };

            if (!string.IsNullOrWhiteSpace(search))
            {
                query = query.Where(x => x.title.Contains(search) || x.url.Contains(search) || x.tag.Contains(search));
                page.TotalCount = query.Count();
                list = query.OrderByDescending(x => x.publish_date).ThenByDescending(x => x.create_time).Skip(pageSize * pageIndex - pageSize).Take(pageSize).ToList();
            }
            else
            {
                page.TotalCount = query.Count();
                list = query.OrderByDescending(x => x.publish_date).ThenByDescending(x => x.create_time).Skip(pageSize * pageIndex - pageSize).Take(pageSize).ToList();
            }

            var cate = DbContext.art_category.OrderByDescending(x => x.is_show).ThenBy(x => x.sort).ThenBy(x => x.create_time).ToList();
            ViewData["page"] = page;
            ViewData["list"] = list;
            ViewData["is_save"] = false;
            ViewData["cate"] = cate;
            ViewData["save-count"] = DbContext.ArticleCrawls.Where(x => x.is_download && x.is_save).Count();
            ViewData["not-save-count"] = DbContext.ArticleCrawls.Where(x => x.is_download && x.is_save == false).Count();

            return View();
        }

        /// <summary>
        /// 获取命理文章
        /// </summary>
        /// <returns></returns>
        public IActionResult GetArticleData(string url = "https://www.sohu.com/tag/67632")
        {
            var result = "已开始执行";
            if (Interlocked.Exchange(ref flag, 1) == 0)
            {
                var uniqueHashList = DbContext.ArticleCrawls.Select(x => x.unique_hash).ToList();
                Task.Factory.StartNew(() =>
                {
                    var list = new List<article_crawler>();
                    ChromeDriver chrome = null;
                    try
                    {
                        chrome = new ChromeDriver(System.IO.Directory.GetCurrentDirectory());
                        chrome.Navigate().GoToUrl(url);
                        for (int i = 0; i < 3; i++)
                        {
                            var js = "var q=document.documentElement.scrollTop=100000";
                            chrome.ExecuteScript(js);
                            var temp = chrome.FindElementsByClassName("news-box");
                            if (temp != null && temp.Count > 1000)
                            {
                                break;
                            }
                            Task.Delay(1 * 1000).Wait();
                        }
                        var newsboxes = chrome.FindElementsByClassName("news-box");
                        if (newsboxes != null && newsboxes.Count > 0)
                        {

                            foreach (var item in newsboxes)
                            {
                                var nameSpanElement = item.FindElements(By.TagName("span")).Where(x => x.GetAttribute("class").Contains("name")).FirstOrDefault();
                                if (nameSpanElement != null && "广告".Equals(nameSpanElement.Text.TrimHtmlChars()))
                                {
                                    continue;   // 无效内容
                                }

                                var href = string.Empty;
                                var title = string.Empty;
                                try
                                {
                                    var aTag = item.FindElement(By.TagName("h4")).FindElement(By.TagName("a"));
                                    href = aTag.GetAttribute("href");
                                    if (href.StartsWith("//"))
                                    {
                                        href = $"https:{href}";
                                    }
                                    if (string.IsNullOrWhiteSpace(href))
                                    {
                                        continue;
                                    }
                                    title = aTag.Text.TrimHtmlChars();
                                    if (string.IsNullOrWhiteSpace(title))
                                    {
                                        continue;
                                    }
                                }
                                catch (Exception ex)
                                {
                                    Console.WriteLine($"爬虫获取标题出现异常\r\n{ex.Message}\r\n{ex.StackTrace}");
                                    continue;
                                }

                                var data = new article_crawler();
                                data.url = href;
                                data.unique_hash = HashTools.sha256(data.url);
                                if (uniqueHashList.Contains(data.unique_hash))
                                {
                                    continue;
                                }

                                var picDivElement = item.FindElements(By.TagName("div")).Where(x => x.GetAttribute("class").Contains("pic")).FirstOrDefault();
                                if (picDivElement != null)
                                {
                                    var coverImg = picDivElement.FindElement(By.TagName("img"));
                                    var coverImgSrc = coverImg.GetAttribute("src");
                                    if (coverImgSrc.StartsWith("//"))
                                    {
                                        coverImgSrc = $"http:{coverImgSrc}";
                                    }
                                    if (Uri.TryCreate(coverImgSrc, UriKind.Absolute, out var imgUri))
                                    {
                                        data.img = ImagesTools.DownloadImg(coverImgSrc);
                                    }
                                }

                                data.title = title;
                                data.is_save = false;
                                data.is_download = false;
                                data.create_time = DateTimeOffset.UtcNow;
                                data.last_update = DateTimeOffset.UtcNow;
                                list.Add(data);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        var err = ex;
                        while (err.InnerException != null)
                        {
                            err = err.InnerException;
                        }
                        Console.WriteLine($"爬虫获取文章列表页出现异常\r\n{err.Message}\r\n{err.StackTrace}");
                    }
                    finally
                    {
                        if (chrome != null)
                        {
                            chrome.Close(); // 关闭浏览器
                        }
                    }

                    var mysql_connstr = Config.GetValue<string>("mysql_connstr");
                    List<article_crawler> articlelist;
                    using (var db = MySqlDbContext.GetDbContext(mysql_connstr))
                    {
                        if (list != null && list.Count > 0)
                        {
                            db.ArticleCrawls.AddRange(list);
                            db.SaveChanges();
                        }

                        articlelist = db.ArticleCrawls.Where(x => x.is_download == false).ToList();
                        var web = new HtmlWeb();
                        foreach (var item in articlelist)
                        {
                            try
                            {
                                var doc = web.Load(item.url);
                                var timeNode = doc.DocumentNode.SelectSingleNode("//span[@id='news-time']");
                                if (DateTimeOffset.TryParse(timeNode.InnerText.TrimHtmlChars(), out var publishTime))
                                {
                                    item.publish_date = publishTime;
                                }

                                var tagListNode = doc.DocumentNode.SelectSingleNode("//span[@class='tag']").Descendants("a");
                                var tagList = tagListNode.Select(x => x.InnerText.TrimHtmlChars()).ToList();
                                item.tag = JsonConvert.SerializeObject(tagList);
                                var articleNode = doc.DocumentNode.SelectSingleNode("//article[@id='mp-editor']");

                                var backNode = articleNode.SelectSingleNode(".//a[@id='backsohucom']");
                                if (backNode != null)
                                {
                                    backNode.ParentNode.Remove();   // 移除返回外部链接
                                }
                                var editorNode = articleNode.Descendants("p").Where(x => x.GetAttributeValue("data-role", string.Empty).Equals("editor-name")).FirstOrDefault();
                                if (editorNode != null)
                                {
                                    editorNode.Remove();
                                }

                                item.content = articleNode.OuterHtml.TrimHtmlChars();
                                item.is_download = true;
                                item.last_update = DateTimeOffset.UtcNow;
                                db.SaveChanges();
                                Task.Delay(1000).Wait();
                            }
                            catch (Exception ex)
                            {
                                var err = ex;
                                while (err.InnerException != null)
                                {
                                    err = err.InnerException;
                                }
                                Console.WriteLine($"爬虫获取文章详情页出现异常\r\n{err.Message}\r\n{err.StackTrace}");
                            }
                        }
                    }

                    Interlocked.Exchange(ref flag, 0);  // 解除锁定
                });
            }
            else
            {
                result = "请不要重复操作,等待上一个任务执行完成";
            }

            ViewData["result"] = result;

            return View();
        }

        /// <summary>
        /// 详情页
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public IActionResult Detail(Guid id)
        {
            var model = DbContext.ArticleCrawls.Where(x => x.id == id).SingleOrDefault() ?? new article_crawler();
            var cate = DbContext.art_category.OrderByDescending(x => x.is_show).ThenBy(x => x.sort).ThenBy(x => x.create_time).ToList();

            if (!string.IsNullOrWhiteSpace(model.content))
            {
                var doc = new HtmlDocument();
                doc.LoadHtml(model.content);
                var articleNode = doc.DocumentNode.SelectSingleNode("//article");
                var backNode = articleNode.SelectSingleNode(".//a[@id='backsohucom']");
                if (backNode != null)
                {
                    backNode.ParentNode.Remove();   // 移除返回外部链接
                }
                var editorNode = articleNode.Descendants("p").Where(x => x.GetAttributeValue("data-role", string.Empty).Equals("editor-name")).FirstOrDefault();
                if (editorNode != null)
                {
                    editorNode.Remove();    // 移除责任编辑
                }

                var imgNodes = articleNode.Descendants("img");
                foreach (var imgNode in imgNodes)
                {
                    var imgSrc = imgNode.GetAttributeValue("src", string.Empty);
                    if (Uri.TryCreate(imgSrc, UriKind.Absolute, out var imgUri))
                    {
                        if (imgUri.Scheme == Uri.UriSchemeHttp || imgUri.Scheme == Uri.UriSchemeHttps)
                        {
                            var imgPath = ImagesTools.DownloadImg(imgSrc);
                            imgNode.SetAttributeValue("src", imgPath);
                        }
                    }
                }
                if (!model.content.Equals(articleNode.OuterHtml))
                {
                    model.content = articleNode.OuterHtml;
                    DbContext.SaveChanges();
                }
            }

            if (model.is_save)
            {
                ViewData["art"] = DbContext.article.Where(x => x.crawler_id == model.id).SingleOrDefault();
            }
            ViewData["article"] = model;
            ViewData["cate"] = cate;

            return View();
        }

        /// <summary>
        /// 更新文章内容
        /// </summary>
        /// <param name="article"></param>
        /// <returns></returns>
        public IActionResult Update(article_crawler article)
        {
            var model = DbContext.ArticleCrawls.Where(x => x.id == article.id).SingleOrDefault();
            if (model != null)
            {
                if (model.is_save)
                {
                    return JsonFailureResult("收录后的文章无须更改");
                }
                model.cateid = article.cateid;
                model.title = article.title;
                model.content = article.content;
                model.last_update = DateTimeOffset.UtcNow;
                DbContext.SaveChanges();
            }
            return JsonOkResult("保存成功");
        }

        /// <summary>
        /// 收录文章内容导致网站
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public IActionResult SaveArticle(Guid id)
        {
            // 判断文章分类是否有效, 如果是被禁用的分类则无效
            var model = DbContext.ArticleCrawls.Where(x => x.id == id && x.is_save == false).SingleOrDefault();
            if (model != null)
            {
                if (model.cateid == Guid.Empty)
                {
                    return JsonFailureResult("请选择分类并保存");
                }
                if (string.IsNullOrWhiteSpace(model.title))
                {
                    return JsonFailureResult("标题不能为空");
                }
                if (string.IsNullOrWhiteSpace(model.content))
                {
                    return JsonFailureResult("文章内容不能为空");
                }
                model.is_save = true;   // 标记为收录状态
                model.last_update = DateTimeOffset.UtcNow;

                var art = new article()
                {
                    cateid = model.cateid,
                    content = model.content,
                    crawler_id = model.id,
                    create_time = DateTimeOffset.UtcNow,
                    img = model.img,
                    is_show = true,
                    keywords = model.title,
                    last_update = DateTimeOffset.UtcNow,
                    sort_num = 100,
                    title = model.title,
                    top = false,
                    uid = LoginUser.Id
                };
                DbContext.article.Add(art);
                DbContext.SaveChanges();
            }
            return JsonOkResult("收录成功");
        }
    }
}